package GUI.calendar;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.ListDataListener;


public class CalendarPanel extends JPanel implements ActionListener {

	private static final long serialVersionUID = 1L;
	private static final Color OFFICE_BLUE = new Color(40, 100, 220);
	private static final Color BACKGROUND = Color.WHITE;
	
	private static HashMap<Integer, String> MONTH_MAP;
	
	private JPanel northPanel;
	private JLabel monthLabel;
	private JPanel monthNavPanel;
	private JPanel yearDropPanel;
	private JComboBox yearComboBox;
	private JPanel monthDropPanel;
	private JComboBox monthComboBox;
	
	private JPanel centerPanel;
	private CalendarGridPanel calendarGridPanel;
	
	private int year = 0;
	private int month = 0;
	
	public CalendarPanel() {
		instantiateMonthMap();
		
		setPreferredSize(new Dimension(600, 400));
		setBackground(Color.WHITE);
		setBorder(BorderFactory.createLineBorder(Color.BLACK, 4));

		setLayout(new BorderLayout());
		add(getNorthPanel(), BorderLayout.NORTH);
		add(getCenterPanel(), BorderLayout.CENTER);
	}
	
	private JPanel getNorthPanel() {
		if (northPanel == null) {
			northPanel = new JPanel();
			northPanel.setLayout(new BorderLayout());
			northPanel.setPreferredSize(new Dimension(600, 90));
			northPanel.setBackground(BACKGROUND);
			
			JPanel monthPanel = new JPanel();
			monthPanel.setLayout(new GridBagLayout());
			monthPanel.setBackground(BACKGROUND);
			monthLabel = new JLabel("MAY 2011");
			monthLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 50));
			monthLabel.setForeground(OFFICE_BLUE);
			monthPanel.add(monthLabel);
			
			northPanel.add(monthPanel, BorderLayout.CENTER);
			
			northPanel.add(getMonthNavPanel(), BorderLayout.SOUTH);
		}
		return northPanel;
	}
	
	private JPanel getMonthNavPanel() {
		if (monthNavPanel == null) {
			monthNavPanel = new JPanel();
			monthNavPanel.setLayout(new BorderLayout());
			
			JPanel blankPanel = new JPanel();
			blankPanel.setBackground(BACKGROUND);
			monthNavPanel.add(blankPanel, BorderLayout.CENTER);
			monthNavPanel.add(getYearDropPanel(), BorderLayout.WEST);
			monthNavPanel.add(getMonthDropPanel(), BorderLayout.EAST);
		}
		return monthNavPanel;
	}
	
	private JPanel getYearDropPanel() {
		if (yearDropPanel == null) {
			yearDropPanel = new JPanel();
			yearDropPanel.setLayout(new GridBagLayout());
			yearDropPanel.setBackground(BACKGROUND);

			GridBagConstraints gbc0 = new GridBagConstraints();
			gbc0.gridx = 0;
			gbc0.gridy = 0;
			yearDropPanel.add(Box.createHorizontalStrut(5), gbc0);
			
			GridBagConstraints gbc1 = new GridBagConstraints();
			gbc1.gridx = 1;
			gbc1.gridy = 0;
			JLabel yearLabel = new JLabel("YEAR  ");
			yearLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 14));
			yearDropPanel.add(yearLabel, gbc1);
			
			GridBagConstraints gbc2 = new GridBagConstraints();
			gbc2.gridx = 2;
			gbc2.gridy = 0;
			yearDropPanel.add(getYearComboBox(), gbc2);
		}
		return yearDropPanel;
	}
	
	private JComboBox getYearComboBox() {
		if (yearComboBox == null) {
			yearComboBox = new JComboBox(new YearComboBoxModel());
			yearComboBox.addActionListener(this);
			yearComboBox.setSelectedIndex(0);
		}
		return yearComboBox;
	}
	
	private JPanel getMonthDropPanel() {
		if (monthDropPanel == null) {
			monthDropPanel = new JPanel();
			monthDropPanel.setLayout(new GridBagLayout());
			monthDropPanel.setBackground(BACKGROUND);
			
			GridBagConstraints gbc1 = new GridBagConstraints();
			gbc1.gridx = 0;
			gbc1.gridy = 0;
			JLabel monthLabel = new JLabel("MONTH  ");
			monthLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 14));
			monthDropPanel.add(monthLabel, gbc1);
			
			GridBagConstraints gbc2 = new GridBagConstraints();
			gbc2.gridx = 1;
			gbc2.gridy = 0;
			monthDropPanel.add(getMonthComboBox(), gbc2);

			GridBagConstraints gbc0 = new GridBagConstraints();
			gbc0.gridx = 2;
			gbc0.gridy = 0;
			monthDropPanel.add(Box.createHorizontalStrut(5), gbc0);
			
		}
		return monthDropPanel;
	}
	
	private JComboBox getMonthComboBox() {
		if (monthComboBox == null) {
			monthComboBox = new JComboBox(new MonthComboBoxModel());
			monthComboBox.addActionListener(this);
		}
		return monthComboBox;
	}
	
	private JPanel getCenterPanel() {
		if (centerPanel == null) {
			centerPanel = new JPanel() {
				private static final long serialVersionUID = 1L;
				
				public void paintComponent(Graphics g) {
					getCalendarGridPanel().setBounds(10, 10, getWidth() - 20, getHeight() - 20);
				}
			};
			centerPanel.setBackground(BACKGROUND);
			centerPanel.setLayout(null);
			
			centerPanel.add(getCalendarGridPanel(), BorderLayout.NORTH);
		}
		return centerPanel;
	}
	
	private CalendarGridPanel getCalendarGridPanel() {
		if (calendarGridPanel == null) {
			dateChanged();
		}
		return calendarGridPanel;
	}
	

	@Override
	public void actionPerformed(ActionEvent evt) {
		if (evt.getSource() == getYearComboBox()) {
			try {
				year = Integer.parseInt((String) getYearComboBox().getSelectedItem());
			} catch (NumberFormatException e) {};
			dateChanged();
		} else if (evt.getSource() == getMonthComboBox()) {
			for (int monthCount : MONTH_MAP.keySet()) {
				if (MONTH_MAP.get(monthCount).equals(getMonthComboBox().getSelectedItem())) {
					month = monthCount;
					break;
				}
			}
			dateChanged();
		}
	}
	
	private void dateChanged() {		
		monthLabel.setText(MONTH_MAP.get(month) + " " + year);
		
		GregorianCalendar date = new GregorianCalendar(year, month, 0);
		int firstDayOfMonth = date.get(Calendar.DAY_OF_WEEK) % 7;
		int daysInMonth = getDaysInMonth(month);
		
		int globalDayCount = 0;
		for (int yearCount = 2011; yearCount < year; yearCount++) {
			globalDayCount += (yearCount % 4 == 0 ? 366 : 365);
		}
		for (int monthCount = 0; monthCount < month; monthCount++) {
			globalDayCount += getDaysInMonth(monthCount);
		}
		
		if (calendarGridPanel == null) {
			calendarGridPanel =
					new CalendarGridPanel(firstDayOfMonth, daysInMonth, globalDayCount);
		} else {
			calendarGridPanel.reset(firstDayOfMonth, daysInMonth, globalDayCount);
		}
	}
	
	private int getDaysInMonth(int month) {
		int daysInMonth = 31;
		switch (month) {
			case 1: daysInMonth = (year % 4 == 0 ? 29 : 28); break;
			case 3: daysInMonth = 30; break;
			case 5: daysInMonth = 30; break;
			case 8: daysInMonth = 30; break;
			case 10: daysInMonth = 30; break;
			default: break;
		}
		return daysInMonth;
	}
	
	
	private void instantiateMonthMap() {
		if (MONTH_MAP == null) {
			MONTH_MAP = new HashMap<Integer, String>();
			MONTH_MAP.put(0, "January");
			MONTH_MAP.put(1, "February");
			MONTH_MAP.put(2, "March");
			MONTH_MAP.put(3, "April");
			MONTH_MAP.put(4, "May");
			MONTH_MAP.put(5, "June");
			MONTH_MAP.put(6, "July");
			MONTH_MAP.put(7, "August");
			MONTH_MAP.put(8, "September");
			MONTH_MAP.put(9, "October");
			MONTH_MAP.put(10, "November");
			MONTH_MAP.put(11, "December");
		}
	}

	
	
	private class YearComboBoxModel implements ComboBoxModel {

		private int selectedYear;
		
		@Override
		public int getSize() {
			return 30;
		}

		@Override
		public Object getElementAt(int index) {
			return String.valueOf(2011 + index);
		}

		public void addListDataListener(ListDataListener l) {}

		public void removeListDataListener(ListDataListener l) {}

		public void setSelectedItem(Object anItem) {
			try {
				selectedYear = Integer.parseInt((String) (anItem));
			} catch (NumberFormatException e) {
				e.printStackTrace();
			}
		}

		public Object getSelectedItem() {
			return String.valueOf(selectedYear);
		}
	}
	
	private class MonthComboBoxModel implements ComboBoxModel {

		private int selectedMonth;
		
		@Override
		public int getSize() {
			return 12;
		}

		@Override
		public Object getElementAt(int index) {
			String monthString = MONTH_MAP.get(index);
			if (monthString == null) { 
				monthString = "Invalid Month";
			}
			return monthString;
		}

		public void addListDataListener(ListDataListener l) {}

		public void removeListDataListener(ListDataListener l) {}

		public void setSelectedItem(Object anItem) {
			for (int month : MONTH_MAP.keySet()) {
				if (MONTH_MAP.get(month).equals(anItem)) {
					selectedMonth = month;
					break;
				}
			}
		}

		public Object getSelectedItem() {
			return MONTH_MAP.get(selectedMonth);
		}
	}
	
}
